package com.itcz.web.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.util.IoUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itcz.maker.generator.main.MakeGenerator;
import com.itcz.maker.generator.main.ZipGenerator;
import com.itcz.maker.meta.Meta;
import com.itcz.maker.meta.MetaValidator;
import com.itcz.maker.template.model.TemplateMakerModelConfig;
import com.itcz.web.annotation.AuthCheck;
import com.itcz.web.common.BaseResponse;
import com.itcz.web.common.DeleteRequest;
import com.itcz.web.common.ErrorCode;
import com.itcz.web.common.ResultUtils;
import com.itcz.web.constant.UserConstant;
import com.itcz.web.exception.BusinessException;
import com.itcz.web.exception.ThrowUtils;
import com.itcz.web.manager.OssManager;
import com.itcz.web.model.dto.generator.*;
import com.itcz.web.model.entity.Generator;
import com.itcz.web.model.entity.User;
import com.itcz.web.model.vo.GeneratorVO;
import com.itcz.web.service.GeneratorService;
import com.itcz.web.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

/**
 * 生成器接口
 *
 * @author <a href="https://github.com/liyupi">奥数定理</a>
 * @from <a href="https://blog.csdn.net/qq_63143493?spm=1000.2115.3001.5343">个人博客</a>
 */
@RestController
@RequestMapping("/generator")
@Slf4j
public class GeneratorController {

    @Resource
    private GeneratorService generatorService;

    @Resource
    private UserService userService;

    @Resource
    private OssManager ossManager;


    // region 增删改查

    /**
     * 创建
     *
     * @param generatorAddRequest
     * @param request
     * @return
     */
    @PostMapping("/add")
    public BaseResponse<Long> addGenerator(@RequestBody GeneratorAddRequest generatorAddRequest,
                                           HttpServletRequest request) {
        if (generatorAddRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Generator generator = new Generator();
        BeanUtils.copyProperties(generatorAddRequest, generator);
        List<String> tags = generatorAddRequest.getTags();
        generator.setTags(JSONUtil.toJsonStr(tags));
        Meta.FileConfig fileConfig = generatorAddRequest.getFileConfig();
        generator.setFileConfig(JSONUtil.toJsonStr(fileConfig));
        Meta.ModelConfig modelConfig = generatorAddRequest.getModelConfig();
        generator.setModelConfig(JSONUtil.toJsonStr(modelConfig));

        // 校验参数
        generatorService.validGenerator(generator, true);
        User loginUser = userService.getLoginUser(request);
        generator.setUserId(loginUser.getId());
        boolean result = generatorService.save(generator);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        long newGeneratorId = generator.getId();
        return ResultUtils.success(newGeneratorId);
    }


    /**
     * 删除
     *
     * @param deleteRequest
     * @param request
     * @return
     */
    @PostMapping("/delete")
    public BaseResponse<Boolean> deleteGenerator(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User user = userService.getLoginUser(request);
        long id = deleteRequest.getId();
        // 判断是否存在
        Generator oldGenerator = generatorService.getById(id);
        ThrowUtils.throwIf(oldGenerator == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可删除
        if (!oldGenerator.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        boolean b = generatorService.removeById(id);
        return ResultUtils.success(b);
    }

    /**
     * 更新（仅管理员）
     *
     * @param generatorUpdateRequest
     * @return
     */
    @PostMapping("/update")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updateGenerator(@RequestBody GeneratorUpdateRequest generatorUpdateRequest) {
        if (generatorUpdateRequest == null || generatorUpdateRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Generator generator = new Generator();
        BeanUtils.copyProperties(generatorUpdateRequest, generator);
        List<String> tags = generatorUpdateRequest.getTags();
        generator.setTags(JSONUtil.toJsonStr(tags));
        Meta.FileConfig fileConfig = generatorUpdateRequest.getFileConfig();
        generator.setFileConfig(JSONUtil.toJsonStr(fileConfig));
        Meta.ModelConfig modelConfig = generatorUpdateRequest.getModelConfig();
        generator.setModelConfig(JSONUtil.toJsonStr(modelConfig));

        // 参数校验
        generatorService.validGenerator(generator, false);
        long id = generatorUpdateRequest.getId();
        // 判断是否存在
        Generator oldGenerator = generatorService.getById(id);
        ThrowUtils.throwIf(oldGenerator == null, ErrorCode.NOT_FOUND_ERROR);
        boolean result = generatorService.updateById(generator);
        return ResultUtils.success(result);
    }

    /**
     * 根据 id 获取代码生成器（封装类）
     *
     * @param id
     * @return
     */
    @GetMapping("/get/vo")
    public BaseResponse<GeneratorVO> getGeneratorVOById(long id) {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Generator generator = generatorService.getById(id);
        if (generator == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        return ResultUtils.success(generatorService.getGeneratorVO(generator));
    }

    /**
     * 分页获取代码生成器列表（仅管理员）
     *
     * @param generatorQueryRequest
     * @return
     */
    @PostMapping("/list/page")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Page<Generator>> listGeneratorByPage(@RequestBody GeneratorQueryRequest generatorQueryRequest) {
        long current = generatorQueryRequest.getCurrent();
        long size = generatorQueryRequest.getPageSize();
        Page<Generator> generatorPage = generatorService.page(new Page<>(current, size),
                generatorService.getQueryWrapper(generatorQueryRequest));
        return ResultUtils.success(generatorPage);
    }

    /**
     * 分页获取代码生成器列表（封装类）
     *
     * @param generatorQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/list/page/vo")
    public BaseResponse<Page<GeneratorVO>> listGeneratorVOByPage(@RequestBody GeneratorQueryRequest generatorQueryRequest,
                                                                 HttpServletRequest request) {
        long current = generatorQueryRequest.getCurrent();
        long size = generatorQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        Page<Generator> generatorPage = generatorService.page(new Page<>(current, size),
                generatorService.getQueryWrapper(generatorQueryRequest));
        return ResultUtils.success(generatorService.getGeneratorVOPage(generatorPage, request));
    }

    /**
     * 分页获取当前用户创建的代码生成器列表
     *
     * @param generatorQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/my/list/page/vo")
    public BaseResponse<Page<GeneratorVO>> listMyGeneratorVOByPage(@RequestBody GeneratorQueryRequest generatorQueryRequest,
                                                                   HttpServletRequest request) {
        if (generatorQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User loginUser = userService.getLoginUser(request);
        generatorQueryRequest.setUserId(loginUser.getId());
        long current = generatorQueryRequest.getCurrent();
        long size = generatorQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        Page<Generator> generatorPage = generatorService.page(new Page<>(current, size),
                generatorService.getQueryWrapper(generatorQueryRequest));
        return ResultUtils.success(generatorService.getGeneratorVOPage(generatorPage, request));
    }

    // endregion

    /**
     * 编辑（用户）
     *
     * @param generatorEditRequest
     * @param request
     * @return
     */
    @PostMapping("/edit")
    public BaseResponse<Boolean> editGenerator(@RequestBody GeneratorEditRequest generatorEditRequest, HttpServletRequest request) {
        if (generatorEditRequest == null || generatorEditRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Generator generator = new Generator();
        BeanUtils.copyProperties(generatorEditRequest, generator);
        List<String> tags = generatorEditRequest.getTags();
        generator.setTags(JSONUtil.toJsonStr(tags));
        Meta.FileConfig fileConfig = generatorEditRequest.getFileConfig();
        generator.setFileConfig(JSONUtil.toJsonStr(fileConfig));
        Meta.ModelConfig modelConfig = generatorEditRequest.getModelConfig();
        generator.setModelConfig(JSONUtil.toJsonStr(modelConfig));

        // 参数校验
        generatorService.validGenerator(generator, false);
        User loginUser = userService.getLoginUser(request);
        long id = generatorEditRequest.getId();
        // 判断是否存在
        Generator oldGenerator = generatorService.getById(id);
        ThrowUtils.throwIf(oldGenerator == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可编辑
        if (!oldGenerator.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        boolean result = generatorService.updateById(generator);
        return ResultUtils.success(result);
    }

    /**
     * 下载代码生成器
     *
     * @param id
     * @param request
     * @param response
     * @return
     */
    @GetMapping("/download")
    public void downloadGeneratorById(long id, HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        User loginUser = userService.getLoginUser(request);
        Generator generator = generatorService.getById(id);
        if (generator == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }

        String filepath = generator.getDistPath();
        if (filepath == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "产物包不存在");
        }

        // 追踪事件
        log.info("用户{}下载了{}", loginUser, filepath);

        InputStream inputStream = null;
        try {
            // 下载文件
            inputStream = ossManager.getObject(filepath);
            // 处理下载到的流
            byte[] bytes = IoUtils.toByteArray(inputStream);
            // 设置响应头
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + filepath);
            // 写入响应
            response.getOutputStream().write(bytes);
            response.getOutputStream().flush();
        } catch (Exception e) {
            log.error("file upload error, filepath = " + filepath, e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "下载失败");
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    /**
     * 使用代码生成器
     *
     * @param generatorUseRequest 使用代码生成器请求对象
     * @param request             Http请求对象
     * @param response            Http响应对象
     * @return
     */
    @PostMapping("/use")
    public void useGenerator(@RequestBody GeneratorUseRequest generatorUseRequest,
                             HttpServletRequest request,
                             HttpServletResponse response) throws IOException {
        // 获取前端传递的参数
        Long id = generatorUseRequest.getId();
        Map<String, Object> dataModel = generatorUseRequest.getDataModel();

        // 需要登陆
        userService.getLoginUser(request);
        // 判断在线使用的代码生成器是否存在
        Generator generator = generatorService.getById(id);
        if (generator == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "代码生成器不存在");
        }

        // 从数据库拿到代码生成器的产物包路径
        String distPath = generator.getDistPath();
        // 判断产物包是否存在
        if (StrUtil.isBlank(distPath)) {
            // 清空响应，避免后续代码写入
            if (!response.isCommitted()) {
                response.reset(); // 重置响应状态和头信息
            }
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "产物包不存在");
        }

        // 定义工作空间，用来存放下载的代码生成器压缩包等临时文件
        String projectPath = System.getProperty("user.dir");
        String tempDirPath = String.format("%s/.temp/generator/use/%s", projectPath, id);

        // 定义压缩包地址
        String zipFilePath = tempDirPath + "/dist.zip";
        // 定义解压后的路径
        File unzipDistDir = new File(tempDirPath + "/dist");

        // 若压缩包所在文件不存在，则创建文件及其所在文件夹
        if (!FileUtil.exist(zipFilePath)) {
            FileUtil.touch(zipFilePath);
            // 下载文件
            try {
                ossManager.getObject(distPath, zipFilePath);
            } catch (Throwable e) {
                // 清空响应，避免后续代码写入
                if (!response.isCommitted()) {
                    response.reset(); // 重置响应状态和头信息
                }
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "代码生成器下载失败");
            }

            // 解压文件
            ZipUtil.unzip(zipFilePath);
        }

        // 将json数据写入json格式的文件中
        String dataModelFilePath = tempDirPath + "/dataModel.json";
        String jsonStr = JSONUtil.toJsonStr(dataModel);
        FileUtil.writeUtf8String(jsonStr, dataModelFilePath);

        // 递归遍历找到脚本文件的路径(通过递归目录找到第一个名称为 “generator.bat” 的文件)， 递归深度为2
        File scriptFile = FileUtil.loopFiles(unzipDistDir, 2, null).stream()
                // 返回第一个名字为generator的文件
                .filter(file -> file.isFile() && "generator.bat".equals(file.getName()))
                .findFirst()
                // 出错时返回运行时异常对象
                .orElseThrow(RuntimeException::new);

        // 如果是非Windows系统，需要给文件添加可行性权限
        try {
            Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("rwxrwxrwx");
            Files.setPosixFilePermissions(scriptFile.toPath(), permissions);
        } catch (Exception e) {

        }

        // 获取脚本的父级目录
        File scriptDir = scriptFile.getParentFile();
        // 如果是Linux，Mac系统，需要使用"./generator"。windows系统使用脚本文件所在的绝对路径
        String scriptAbsolutePath = scriptFile.getAbsolutePath().replace("\\", "/");
        // 构造命令
        String[] commands = new String[]{scriptAbsolutePath, "json-generate", "--file=" + dataModelFilePath};

        // 根据命令创建ProcessBuilder对象
        ProcessBuilder processBuilder = new ProcessBuilder(commands);
        // 指出执行命令所在路径
        processBuilder.directory(scriptDir);

        try {
            // 执行命令
            Process process = processBuilder.start();

            //读取执行命令的输出
            InputStream inputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // 等待命令执行完成
            int exitCode = process.waitFor();
            System.out.println("命令执行完成，退出码：" + exitCode);
        } catch (Exception e) {
            // 清空响应，避免后续代码写入
            if (!response.isCommitted()) {
                response.reset(); // 重置响应状态和头信息
            }
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "执行命令失败");
        }

        // 执行脚本后，代码生成器的路径
        String generatedPath = scriptDir + "/generated";
        // 压缩后的代码生成器文件路径
        String resultPath = tempDirPath + "/result.zip";
        // 压缩代码生成器文件
        File resultFile = ZipUtil.zip(generatedPath, resultPath);

        // 将压缩后的代码生成器文件返回给前端
        // 设置响应头
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename=" + resultFile.getName());
        // 写入响应
        Files.copy(resultFile.toPath(), response.getOutputStream());
        // 异步清理临时文件
        CompletableFuture.runAsync(() -> {
            FileUtil.del(tempDirPath);
        });
    }

    /**
     * 制作代码生成器
     *
     * @param generatorMakeRequest 制作代码生成器请求对象
     * @param request              Http请求对象
     * @param response             Http响应对象
     * @return
     */
    @PostMapping("/make")
    public void makeGenerator(@RequestBody GeneratorMakeRequest generatorMakeRequest,
                              HttpServletRequest request,
                              HttpServletResponse response) throws IOException {
        String tempDirPath = null;
        try {
            // 获取前端传入的参数
            Meta meta = generatorMakeRequest.getMeta();
            String zipFilePath = generatorMakeRequest.getZipFilePath();

//            // 如果默认值为“false”或"true"，则转换成boolean类型的false或true(前端已经实现）
//            if (meta.getModelConfig().getModels() != null) {
//                List<Meta.ModelConfig.ModelInfo> modelInfoList = meta.getModelConfig().getModels();
//                for (int i = 0; i < modelInfoList.size(); i++) {
//                    Meta.ModelConfig.ModelInfo modelInfo = modelInfoList.get(i);
//                    // 分组模型配置
//                    if(modelInfo.getGroupKey() != null) {
//                        List<Meta.ModelConfig.ModelInfo> groupModelInfoList = modelInfo.getModels();
//                        for (int j = 0; j < groupModelInfoList.size(); j++) {
//                            Meta.ModelConfig.ModelInfo groupModelInfo = groupModelInfoList.get(i);
//                            // 处理模型配置
//                            handleModelInfo(groupModelInfo, groupModelInfoList, i);
//                        }
//                        modelInfoList.set(i, modelInfo);
//                    } else {
//                        // 未分组文件配置
//                        // 处理模型配置
//                        handleModelInfo(modelInfo, modelInfoList, i);
//                    }
//                }
//
//            }

            // 需要登陆
            userService.getLoginUser(request);

            // 1）判断模板文件路径是否正确
            if (StrUtil.isBlank(zipFilePath)) {
                // 清空响应，避免后续代码写入
                if (!response.isCommitted()) {
                    response.reset(); // 重置响应状态和头信息
                }
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "模板文件路径不存在");
            }

            // 2）定义工作空间，用来存放下载的模板文件、其他临时文件
            // 项目根路径
            String projectDir = System.getProperty("user.dir");
            // 随机id，确保每个制作过程都拥有独立的工作空间
            String id = IdUtil.getSnowflakeNextId() + RandomUtil.randomString(6);
            // 单次制作的工作空间目录
            tempDirPath = String.format("%s/.temp/generator/make/%s", projectDir, id);

            // 3）从对象存储OSS中下载项目模板文件
            // 项目模板文件的本地路径
            String localZipFilePath = tempDirPath + File.separator + "template.zip";
            // 判断本地路径是否存在
            if (!FileUtil.exist(localZipFilePath)) {
                FileUtil.touch(localZipFilePath);
            }
            // 下载项目模板文件到本地路径
            try {
                ossManager.getObject(zipFilePath, localZipFilePath);
            } catch (Throwable e) {
                // 清空响应，避免后续代码写入
                if (!response.isCommitted()) {
                    response.reset(); // 重置响应状态和头信息
                }
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "项目模板文件下载失败");
            }

            // 4）解压文件
            File unzipDistDir = ZipUtil.unzip(localZipFilePath);

            // 5）构造代码生成器制作工具所需要的参数，包括Meta对象和代码生成器输出路径
            String sourceRootPath = unzipDistDir.getAbsolutePath();
            // 填充资源根路径
            meta.getFileConfig().setSourceRootPath(sourceRootPath);
            // 校验并填充元数据
            MetaValidator.doValidWithFill(meta);
            // 构建输出路径
            String outputFilePath = String.format("%s/generated/%s", tempDirPath, meta.getName());

            // 6) 调用制作工具
            MakeGenerator makeGenerator = new ZipGenerator();
            try {
                makeGenerator.doGenerate(meta, outputFilePath);
            } catch (Exception e) {
                // 清空响应，避免后续代码写入
                if (!response.isCommitted()) {
                    response.reset(); // 重置响应状态和头信息
                }
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "代码生成器制作失败");
            }

            // 7)返回制作好的代码生成器压缩包
            String suffix = "-dist.zip";
            // 压缩包名字
            String zipFileName = meta.getName() + suffix;
            // 压缩包所在路径
            String distZipFilePath = outputFilePath + suffix;

            // 下载文件
            // 设置响应头
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + zipFileName);
            // 写入响应
            Files.copy(Paths.get(distZipFilePath), response.getOutputStream());
        } finally {
            // 同步删除临时文件
            FileUtil.del(tempDirPath);
        }
    }

//    /**
//     * 处理前端传入的"true"和"false"字符串,将其转为对应的布尔值（前端已实现）
//     *
//     * @param modelInfo 模型配置信息
//     * @param modelInfoList 模型配置信息列表
//     * @param i 用于遍历列表
//     */
//    private static void handleModelInfo(Meta.ModelConfig.ModelInfo modelInfo, List<Meta.ModelConfig.ModelInfo> modelInfoList, int i) {
//        Object defaultValue = modelInfo.getDefaultValue();
//        // 仅处理字符串类型的默认值
//        if (defaultValue instanceof String) {
//            String strValue = (String) defaultValue;
//            // 使用 equalsIgnoreCase 处理大小写，并去除空格
//            if ("false".equalsIgnoreCase(strValue.trim())) {
//                modelInfo.setDefaultValue(false);
//            } else if ("true".equalsIgnoreCase(strValue.trim())) {
//                modelInfo.setDefaultValue(true);
//            }
//            // 将处理完成后的模型配置设置到相应的位置
//            modelInfoList.set(i, modelInfo);
//        }
//    }

}
